package sad_G212;

import java.util.ArrayList;
import weka.classifiers.lazy.IBk;
import weka.classifiers.meta.CVParameterSelection;
import weka.classifiers.meta.GridSearch;
import weka.core.ChebyshevDistance;
import weka.core.DistanceFunction;
import weka.core.EuclideanDistance;
import weka.core.ManhattanDistance;
import weka.core.SelectedTag;
import weka.core.neighboursearch.LinearNNSearch;

public class KNN {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {

		IBk miClas;
		Evaluadores miEval = new Evaluadores();
		UtilArchivos ua = new UtilArchivos();

		if (ua.parametrosCorrectos(args)){

			String mejorDist = ""; 
			String mejorFact = "";
			int mejorkNN = 0;
			double mejorfmeasure = 0;

			ua.leerArchivo(args[0]);
			Datos misDatos = new Datos(ua.getArchivo());	//Cogemos el archivo de datos
			ua.cerrarArchivo();

			// Creamos un array con las funciones
			// de distancia que vamos a probar
			ArrayList<DistanceFunction> busquedasLineales = new ArrayList<DistanceFunction>();

			busquedasLineales.add(new ManhattanDistance());
			busquedasLineales.add(new EuclideanDistance());
			busquedasLineales.add(new ChebyshevDistance());

			// Creamos un array con los factores
			// de ponderacion que vamos a probar
			ArrayList<SelectedTag> factPonderacion = new ArrayList<SelectedTag>();

			factPonderacion.add(new SelectedTag(IBk.WEIGHT_NONE, IBk.TAGS_WEIGHTING));
			factPonderacion.add(new SelectedTag(IBk.WEIGHT_INVERSE, IBk.TAGS_WEIGHTING));
			factPonderacion.add(new SelectedTag(IBk.WEIGHT_SIMILARITY, IBk.TAGS_WEIGHTING));

			//Por cada busqueda lineal
			for (DistanceFunction distancia : busquedasLineales) {
				LinearNNSearch busqLineal= new LinearNNSearch();
				busqLineal.setDistanceFunction(distancia);

				//Por cada factor de ponderación
				for (SelectedTag factor : factPonderacion){
					System.out.println("Probando con distancia:" + distancia.getClass().toString() + " y factor \"" + factor.getSelectedTag().getReadable() + "\"");
					//Haremos KNN desde 1 hasta N/5, por probar
					for (int i = 1; i < misDatos.getNumInstancias()/5; i++) {		

						Datos datosActual = new Datos(new UtilArchivos(args[0]).getArchivo());	//Creamos una nueva instancia de datos
						datosActual.mezclarInstancias();										//Mezclamos las instancias

						miEval.setDatos(datosActual.getDatos());				//pasamos los datos al evaluador

						miClas = new Clasificadores().clasificadorKNN(i);		//Creamos el clasificador kNN
						miClas.setNearestNeighbourSearchAlgorithm(busqLineal);	//Asignamos el tipo de busqueda lineal 
						miClas.setDistanceWeighting(factor);					//Asignamos el factor de ponderación

						miEval.crossValidation(miClas, datosActual.getDatos(), 10);	//Clasificamos los datos

						double wfm = miEval.getEvaluador().weightedFMeasure(); //Obtenemos el fmeasure

						//Comparamos y nos quedamos con el mejor fmeasure y sus parametros
						if (wfm > mejorfmeasure){
							mejorfmeasure = wfm;
							mejorDist = distancia.getClass().toString();
							mejorFact = factor.getSelectedTag().getReadable();
							mejorkNN = i;
						}
					}
				}
			}
			//Mostramos el resultado
			System.out.println("Mejores resultados obtenidos:\n - Distancia: " + mejorDist + "\n - Factor: " + mejorFact + "\n - kNN: " + mejorkNN + "\n - fMeasure: " + mejorfmeasure);
			System.out.println();
			System.out.println("Mejor opción según CVParam:");

			//comprobamos el resultado con CVParam
			CVPar(args[0]);

			System.out.println();
			System.out.println("Mejor opción según CVGridSearch:");
			GrdSrch(args[0]);
		}

	}

	public static void CVPar(String pRuta) throws Exception {

		CVParameterSelection selecPar;
		Clasificadores miClas = new Clasificadores();
		UtilArchivos ua = new UtilArchivos();
		String resul = "";

		ua.leerArchivo(pRuta);
		Datos misDatos = new Datos(ua.getArchivo());	//Cogemos el archivo de datos
		ua.cerrarArchivo();

		misDatos.mezclarInstancias();

		Integer numInst = misDatos.getNumInstancias()/5;

		selecPar = new CVParameterSelection();							//Creamos el selector
		selecPar.setClassifier(miClas.clasificadorKNN());				//Le asignamos el clasificador
		selecPar.addCVParameter("K 1.0 " + numInst + " " + numInst );//Probaremos el parámetro K desde 1 a 10, de 1 en 1
		selecPar.setNumFolds(10);										//Establecemos el número de "folds"
		selecPar.buildClassifier(misDatos.getDatos());					//Generamos el resultado óptimo

		for (String string : selecPar.getBestClassifierOptions()) {	//Recogemos el String[] que devuelve
			resul += string + " ";				
		}

		System.out.println(resul);									//Lo mostramos por consola

	}

	public static void GrdSrch(String pRuta){
		UtilArchivos ua = new UtilArchivos();

		ua.leerArchivo(pRuta);
		Datos misDatos = new Datos(ua.getArchivo());	//Cogemos el archivo de datos
		ua.cerrarArchivo();

		try{
			GridSearch miGS = new GridSearch();			//Creamos el GridSearch
			miGS.setClassifier(new IBk());				//Establecemos el clasificador
			miGS.buildClassifier(misDatos.getDatos());	//Buscamos la configuración óptima
			//------------------------------------------//Este filtro no permite tener varios atributos nominales
			//------------------------------------------//así que no funcionará y tendremos que recoger el error
		}catch(Exception e){
			e.printStackTrace();
		}


	}

}
